package com.biz.primus.base.global;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Sets;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 *
 * 全局参数引用处理器
 * @author yanweijin
 * @date 2017/11/18
 */
public class GlobalRefHandler {


	private GlobalRefHandler(){}

	private static final Set<Class> ignoreClasses = Sets.newHashSet();

	private static final LoadingCache<Class, List<Pair<Field, GlobalValue>>> CACHE = CacheBuilder.newBuilder().maximumSize(200)
		.expireAfterWrite(15, TimeUnit.MINUTES).build(new CacheLoader<Class, List<Pair<Field, GlobalValue>>>() {
			@Override
			public List<Pair<Field, GlobalValue>> load(Class clazz) throws Exception {
				List<Field> fields = FieldUtils.getFieldsListWithAnnotation(clazz, Ref.class);
				return fields.stream().map(f -> Pair.of(f, f.getAnnotation(Ref.class).value()))
						.collect(Collectors.toList());
			}
		});

	/**
	 * 将全局参数中的某些属性复制到target对应字段中
	 * 需要在target字段上标注@Ref注解并指定引用的属性
	 * @param gp
	 * @param target
	 */
	public static void handle(GlobalParams gp, Object target){
		if(target==null){
			return;
		}
		Class<?> clazz = target.getClass();
		if (ignoreClasses.contains(clazz)) {
			//在忽略列表,直接略过
			return;
		}
		List<Pair<Field, GlobalValue>> pairs;
		try {
			pairs = CACHE.get(clazz);
		} catch (ExecutionException e) {
			e.printStackTrace();
			throw new RuntimeException("反射引用全局参数时出现异常",e);
		}
		if(CollectionUtils.isEmpty(pairs)){
			//类中没有Ref注解,加入忽略列表
			ignoreClasses.add(clazz);
			return;
		}

		//对于被Ref标注的字段,从全局参数中获取对应值写入
		pairs.forEach(p->{
//			Class<?> fieldType = p.getKey().getType();
//			if(fieldType.isAssignableFrom(p.getRight().fieldType)){
//			}
			ReflectionUtils.makeAccessible(p.getKey());
			ReflectionUtils.setField(p.getKey(),target,getGlobalParam(gp,p.getValue()));
		});
	}

	private static Object getGlobalParam(GlobalParams gp, GlobalValue gv){
		switch (gv) {
			case addressLat:return gp.getAddressLat();
			case addressLon:return gp.getAddressLon();
			case cityId:return gp.getCityId();
			case depotCode:return gp.getDepotCode();
			case depotId:return gp.getDepotId();
			case lat:return gp.getLat();
			case lon:return gp.getLon();
			case memberId:return gp.getMemberId();
			case globalParams:return gp;
			case provinceId:return gp.getProvinceId();
			default:
				throw new RuntimeException("错误的代码:没有对GlobalValue["+gv+"]做映射");
		}
	}

}
